package org.epsi.blokus.server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Observable;
import java.util.Stack;
import org.epsi.blokus.commun.Message;

/**
 * Relations avec un client
 * @author julien.lafont
 */
public class ServerClient extends Observable implements Runnable {

    private static enum TEtat { RUN, STOP };

    private static int numerotationClients = 0;

     // Etat du thread
    private TEtat etat;

    // Numéro du client
    private int index;

    // Salon auquel appartient le client
    private Salon salon = null;

    // Le client est-il connecté ?
    private Boolean est_connecte;

    // Objets de communication réseau
    private Socket socket;
    private ObjectOutputStream out;
    private ObjectInputStream in;

    public ServerClient(Socket sock) throws IOException
    {
        socket = sock;
        etat = TEtat.RUN;
        est_connecte = true;

        // Initialisation des tuyaux de communication
        out = new ObjectOutputStream(socket.getOutputStream());
        in = new ObjectInputStream(socket.getInputStream());

        // Numéro du client
        index = numerotationClients++;
    }


    /**
     * Ecoute de manière asynchrone 
     */
    public void run()
    {
        try {

            Stack<Message> pileMessage  = new Stack<Message>();
            
            // Boucle principale du ServeurClient : tourne tant qu'aucun message d'arrêt n'a été recu
            // et tant qu'aucune erreur n'est survenue.
            while(etat == TEtat.RUN)
            {
                 
                 Message mess;
                 while (est_connecte && (mess = (Message)in.readObject()) != null)
                 {
                    pileMessage.addElement(mess);

                    //System.out.println(mess.debug());

                    // On notifie le serveur du message reçu
                    setChanged();
                    notifyObservers(pileMessage.pop().clone());
                    clearChanged();

                    System.out.println("Fin");

                    if (mess.getTypeMessage() == Message.TTypeMessage.deconnexion_client) est_connecte = false;
                      
                 }

                 etat = TEtat.STOP;
            }

         // Une erreur de communication équivaut à la déconnexion du client
         } catch (Exception ex) {

            etat = TEtat.STOP;

            System.out.println("Erreur client : "+ex.getMessage());
            ex.printStackTrace();
            
            // On indique au serveur que le client est déconnecté
            setChanged();
            notifyObservers(new Message(Message.TTypeMessage.deconnexion_client));

        // Fermer toutes les connexions à la fin du thread
        } finally {
            
            // Fermeture de la socket
            forcerFermetureSocket();
        }
         
    }

    /**
     * Envoyer un message au client
     * @param m
     * @return résultat de l'opération
     */
    public synchronized Boolean envoyerMessage(Message m)
    {
        try {
            
            this.out.writeObject(m);
            return true;
        } catch(Exception e) {
            return false;
        }
    }

    /**
     * Ferme la socket 
     */
    private void forcerFermetureSocket()
    {
        try {
            in.close();
            out.close();
            socket.close();

        } catch(IOException exNonTraitee) {}
    }


    /**
     * Retourne le numéro du joueur
     * @return
     */
    public int getNumero()
    {
        return index;
    }

    /**
     * Défini le salon du joueur
     * @param s
     */
    public void setSalon(Salon s)
    {
        this.salon = s;
    }

    /**
     * Retourne le salon du joueur
     * @return
     */
    public Salon getSalon()
    {
        return this.salon;
    }

    /**
     * Déconnecte le joueur
     */
    public void deconnecter() {
        this.etat = TEtat.STOP;
    }


}
